package handler

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"math"
	"time"

	"kop/framework/broker"
	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/libs/redis"
	"kop/modules/item/consts"
	"kop/modules/money/configuration"
	"kop/modules/money/entity"
	"kop/modules/money/models"
	"kop/modules/role/configuration/level"
	"kop/pb"
	"kop/pb/service"
	"kop/util/log"
	"kop/util/rand"

	activityC "kop/modules/activity/client"
	itemC "kop/modules/item/client"
	roleC "kop/modules/role/client"
)

type MoneyServiceHandler struct {
	context2.Context
}

func (h *MoneyServiceHandler) Online(_ context.Context, req *service.MoneyOnlineRequest, resp *pb.MoneyResp) error {

	var conf, _ = level.GetByRoleLevel(req.Peerage)

	var money = models.FirstOrCreate(database.DB, req.RoleID, req.ServerID)

	resp.Sec = int64(time.Now().Sub(money.UpdatedAt).Seconds())
	resp.Coin = int64(math.Min(float64(resp.Sec), float64(conf.OfflineTime)*60)) * money.Income

	money.Coin += resp.Coin

	resp.Money = buildPbMoney(money)
	return models.Save(database.DB, &money)
}

func (h *MoneyServiceHandler) SubCoin(ctx context.Context, req *wrappers.Int64Value, resp *pb.Money) (err error) {

	if req.Value < 1 {
		return errors.ParamError
	}

	var roleID = h.GetRoleID(ctx)
	var money = h.getCoin(roleID)
	if money.Coin < req.Value {
		return errors.CoinNotEnoughError
	}

	money.Coin -= req.Value

	if err = models.Save(database.DB, &money); err == nil {

		activityC.AddPoints(roleID, h.GetServerID(ctx), activityC.Coin, req.Value)

		*resp = *buildPbMoney(money)
	}

	return err
}

func (h *MoneyServiceHandler) AsyncSubCoin(ctx context.Context, req *wrappers.Int64Value, _ *empty.Empty) error {

	var money pb.Money
	var err = h.SubCoin(ctx, req, &money)
	if err == nil {
		_ = broker.RolePublish(h.GetRoleID(ctx), pb.WsCode_CodeMoney, &money)
	}

	return err
}

func (h *MoneyServiceHandler) AddCoin(_ context.Context, req *service.CoinRequest, resp *pb.Money) error {

	if req.Coin < 1 {
		return errors.ParamError
	}

	var money = h.getCoin(req.RoleID)
	money.Coin += req.Coin

	*resp = *buildPbMoney(money)
	return models.Save(database.DB, &money)
}

func (h *MoneyServiceHandler) Offline(_ context.Context, req *service.RoleRequest, _ *empty.Empty) error {

	var money = h.getCoin(req.RoleID)
	return models.Save(database.DB, &money)
}

func (h *MoneyServiceHandler) GetIncome(_ context.Context, req *service.RoleRequest, resp *wrappers.Int64Value) error {

	return database.DB.Model(&entity.Money{}).Select("income AS \"value\"").Where("role_id = ?", req.RoleID).Scan(resp).Error
}

func (h *MoneyServiceHandler) GetMaxIncome(_ context.Context, req *service.RoleRequest, resp *wrappers.Int64Value) error {

	return database.DB.Model(&entity.Money{}).Select("max_income AS \"value\"").Where("role_id = ?", req.RoleID).Scan(resp).Error
}

func (h *MoneyServiceHandler) UpdateIncome(_ context.Context, req *service.CoinRequest, _ *empty.Empty) (err error) {

	if req.Coin < 1 {
		return errors.ParamError
	}

	var money = models.GetMoney(database.DB, req.RoleID)

	var up = req.Coin - money.MaxIncome

	money.Income = req.Coin
	if money.Income > money.MaxIncome {
		money.MaxIncome = money.Income
		money.IncomeUpdatedAt = time.Now()
	}

	if err = models.Save(database.DB, &money); err != nil {
		return err
	}

	if up > 0 {
		activityC.AddPoints(money.RoleID, money.ServerID, activityC.Income, up)
	}
	return nil
}

func (h *MoneyServiceHandler) UseRum(ctx context.Context, _ *empty.Empty, resp *pb.UseRumResp) error {

	const NeedRum = 100

	var roleID = h.GetRoleID(ctx)

	var item, err = itemC.DelItem(roleID, h.GetServerID(ctx), consts.Rum, NeedRum)
	if err != nil {
		return err
	}

	var n = redis.GetTodayNumber(fmt.Sprintf("UseRumGrade4_%d", roleID))
	var luck = n < int(roleC.GetVIP(roleID).Benediction)

	var configs []rand.Value

	for _, v := range configuration.Configs {

		if v.GetID() == 4 && !luck { // 特殊处理
			continue
		}

		configs = append(configs, v)
	}

	var conf = rand.WithWeight(configs, 1)[0].(configuration.Conf)
	if conf.GetID() == 4 {
		redis.SetTodayNumber(fmt.Sprintf("UseRumGrade4_%d", roleID), n+1)
	}

	resp.Grade = conf.GetID()
	resp.Morale = conf.Morale + rand.Int32(0, 9)
	if resp.Morale == 99 && rand.IsWin(0.5) {
		resp.Morale = 100
	}

	// 【银币收益】= 银币基础收益 * (100 + 当前士气 - 当前评价最低士气) / 100
	var money = h.getCoin(roleID)
	var income = money.Income
	var coin = int64(float64(income*conf.Income) / 100 * float64(100+resp.Morale-conf.Morale))
	money.Coin += coin

	resp.Award = &pb.Award{
		Coin:       coin,
		Money:      buildPbMoney(money),
		AwardItems: []*pb.AwardItem{{Item: item}},
	}

	return models.Save(database.DB, &money)
}

func (h *MoneyServiceHandler) Ranking(_ context.Context, req *wrappers.Int32Value, resp *service.RankingPointsList) error {

	var data []entity.Money

	database.DB.Where("server_id = ? AND income > 0", req.Value).Order("income DESC, income_updated_at ASC").Limit(200).Find(&data)

	resp.Ranking = make([]*service.RankingPoints, 0, len(data))
	for _, v := range data {
		resp.Ranking = append(resp.Ranking, &service.RankingPoints{RoleID: v.RoleID, Points: v.Income})
	}

	return nil
}

func (h *MoneyServiceHandler) MyRanking(ctx context.Context, _ *empty.Empty, resp *pb.RankingNumber) (err error) {

	var money = models.GetMoney(database.DB, h.GetRoleID(ctx))

	var number int32
	database.DB.Model(&money).Where("server_id = ? AND income > ?", h.GetServerID(ctx), money.Income).Count(&number)

	resp.Points = money.Income
	resp.Number = number + 1
	return nil
}

func (h *MoneyServiceHandler) getCoin(roleID int32) entity.Money {

	var money = models.GetMoney(database.DB, roleID)

	log.Info("隐形增加银币 %d X %d  = %d", int64(time.Now().Sub(money.UpdatedAt).Seconds()), money.Income, int64(time.Now().Sub(money.UpdatedAt).Seconds())*money.Income)

	money.Coin += int64(time.Now().Sub(money.UpdatedAt).Seconds()) * money.Income

	return money
}

func buildPbMoney(money entity.Money) *pb.Money {

	return &pb.Money{
		Coin:   money.Coin,
		Income: money.Income,
	}
}
